package com.jamie.jvm.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author jamie
 *
 *         题目：实现一个自旋锁
 *         自旋锁的好处：循环比较获取直到成功为止，没有类似wait的阻塞。
 *
 *         通过CAS操作完成自旋锁，A线程先进来调用myLock方法自己持有锁5秒钟，
 *         B随后进来后发现当前线程持有锁，不是null，所以只能通过自旋等待，直到A释放锁后B随后抢到。
 */
public class SpinLockDemo {

    /** 原子引用线程 */
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t invoked myLock()");
        while(!atomicReference.compareAndSet(null, thread)){
            // 因为输出循环中太多，添加睡眠减少输出值
            threadSleep(1);
            System.out.println(Thread.currentThread().getName() + "\t 一直循环中");
        }

    }

    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t invoked myUnLock()");
        atomicReference.compareAndSet(thread, null);
    }

    private static void threadSleep(int i) {
        try{
            TimeUnit.SECONDS.sleep(i);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        // AA 线程锁住，5秒钟后解锁
        new Thread(() -> {
            spinLockDemo.myLock();
            threadSleep(5);
            spinLockDemo.myUnLock();
        }, "AA").start();
        threadSleep(1);
        // BB线程一直卡在在获取锁，直到AA解锁以后，才获得锁，所以一直输出循环中。直到AA解锁后，BB一秒后才解锁
        new Thread(() -> {
            spinLockDemo.myLock();
            threadSleep(1);
            spinLockDemo.myUnLock();
        }, "BB").start();
    }

}
